unit unit_GenesisLocalRepository;

interface
uses
  SysUtils,
  Windows,
  Classes,
  StrUtils,
  DB,
  //
  // my units
  //
  unit_CSVFile,
  unit_DBStructure,
  unit_GenesisDBConsts,
  //
  // 3rd party units
  //
  kbmMemTable,
  kbmMemBinaryStreamFormat,
  MD5,
  unit_MD5Misc
  ;

type
  //
  // Supplementaires TGenesisLocalRepository class types
  //
  TRepository = record
    BaseDir: AnsiString;
    MinID: Integer;
    MaxID: Integer;
    //
    procedure Clear;
  end;
  TRepositories = array of TRepository;

type
  //
  // Genesis DB record file information structure
  //
  PGenesisRecord = ^TGenesisRecord; 
  TGenesisRecord = record
  private
  public
    ID: Integer;
    FileName: AnsiString;
    FileSize: Int64;
    MD5: AnsiString;
    CRC32: AnsiString;
    IsLocal: Boolean;
    RootDir: AnsiString;
    //
    function FullName: AnsiString;
    function CheckRecord: Boolean;
    procedure Clear;
  end;
//
type
  TCheckCondition = (cstStateUnknown = 1, cstFileName, cstFileSize,
      cstMD5, cstCRC32);
  TCheckState = set of TCheckCondition;
  TFileFlag = (fstUnknown = 1, fstBadID, fstOutOfRepIdRange, fstFileExists,
      fstFileSize, fstMD5, fstCRC32, fstOutdatedFile, fstExtraFile, fstAccessDenied);
  TFileState = set of TFileFlag;
//==============================================================================
// TGenesisLocalRepository class
//==============================================================================
  TGenesisLocalRepository = class(TObject)
  private
    FFileChecks: TCheckState;
    //
    // Repositories location information
    //
    FRepositories: TRepositories;
    FRepositoriesCount: Integer;
    //
    // Repository's file information arrays
    //
    FCSVFile: TCSVFile;
    FGenesisTable: TkbmMemTable;
    FDumpFormat: TkbmBinaryStreamFormat;
    //
//    FGenesisFiles: array of TGenesisRecord;
//    FGenesisFilesCapacity: Integer;
//    FGenesisFilesCount: Integer;
    //
    FCurrentFileRecord: TGenesisRecord;
    FDataLoaded: Boolean;
    FDataSource: TDataSource;
    FChecking: Boolean;
    FProcessedFiles: TStringList;
    FTableActive: Boolean;
    FTotalFilesCount: Integer;
    function GetDataSource: TDataSource;
    //--------------------------------------------------------------------------
    // Property variables get/set functions
    //--------------------------------------------------------------------------
    //
    // property Repositories
    //
    function GetRepositories(Index: Integer): TRepository;
    procedure SetRepositories(Index: Integer; const Value: TRepository);
    //
    // property Files
    //
    function GetGenesisRecord(FileID: Integer): TGenesisRecord;
    function GetTableActive: Boolean;
    function GetTotalFilesCount: Integer;
    procedure SetChecking(const Value: Boolean);
    procedure SetFileChecks(const Value: TCheckState);
    procedure SetGenesisFiles(FileID: Integer; const Value: TGenesisRecord);
    procedure SetTableActive(const Value: Boolean);
  protected
    function GetLocalRepositoryPath(const GenesisID: Integer): AnsiString;
    //
    // Table structure creation related routines
    //
    function CreateTables: Boolean;
    function CreateTable(var Table: TkbmMemTable; const TableName: WideString;
        const FieldDesc: array of TFieldDesc;
        const IndexDesc: array of TIndexDesc): Boolean;
    function GetAbsoluteFileName(const BaseDir, RelFileName: AnsiString):
        AnsiString;
  {protected section}
  { TODO -oAdministrator -c : Duplicate functions, define preferred function name 11.08.2010 20:52:44 }
    function GetLocalRepositoryDir(const GenesisID: Integer): AnsiString;
    property Checking: Boolean read FChecking write SetChecking;
  public
    //
    // Common class functions - constructor, destructor, etc
    //
    constructor Create;
    destructor Destroy; //override;
    procedure ClearData;
    //
    // Repositories related routines and variables
    //
    property Repositories[Index: Integer]: TRepository read GetRepositories
        write SetRepositories;
    property RepositoriesCount: integer read FRepositoriesCount;
    //
    procedure AddRepository(const NewRepository: TRepository);
    procedure ClearRepositories;
    //
    // DB record information routines and variables
    //
    property Files[FileID: Integer]: TGenesisRecord read GetGenesisRecord
        write SetGenesisFiles;
//    property FilesCount: Integer read FGenesisFilesCount;
    property DataLoaded: Boolean read FDataLoaded;
    property DataSource: TDataSource read GetDataSource;
    property TableActive: Boolean read GetTableActive write SetTableActive;
    property TotalFilesCount: Integer read GetTotalFilesCount;

    //
    // Genesis data processing routines
    //
    function LoadDataFromCSV(const CSVFileName: AnsiString): Boolean;
    function LoadDataFromDump(const DumpFile: AnsiString): Boolean;
    function SaveDataToDump(const DumpFile: AnsiString): Boolean;
    //
    procedure ClearGenesisFiles;

    function FileNameToID(const FileName: AnsiString): Integer;
    function LoadGenesisRecord(const FileID: Integer): Boolean;
    //
    procedure StartCheck;
    procedure EndCheck;
    function CheckFile(const AbsFileName: AnsiString; var FileState: TFileState;
        var ID: Integer): Boolean;
    property FileChecks: TCheckState read FFileChecks write SetFileChecks;       
  end;
implementation
//------------------------------------------------------------------------------
// constructor TGenesisLocalRepository.Create
//   Create exemplar of TGenesisLocalRepository class
// Parameters:
//   None
// Result:
//   None
//------------------------------------------------------------------------------
constructor TGenesisLocalRepository.Create;
begin
  inherited;
  //
  // initialize interanl flag variables
  //
  FDataLoaded := False;
  FTableActive := False;
  //
  // Initialize in-memory table save format settings
  //
  FDumpFormat := TkbmBinaryStreamFormat.Create(nil);
  with FDumpFormat do begin
    Name := 'sfBinary';
    Version := '3.00';
    sfUsingIndex := [sfSaveUsingIndex];
    sfData := [sfSaveData, sfLoadData];
    sfCalculated := [];
    sfLookup := [];
    sfNonVisible := [sfSaveNonVisible, sfLoadNonVisible];
    sfBlobs := [sfSaveBlobs, sfLoadBlobs];
    sfDef := [sfSaveDef, sfLoadDef];
    sfIndexDef := [sfSaveIndexDef, sfLoadIndexDef];
    sfFiltered := [sfSaveFiltered];
    sfIgnoreRange := [sfSaveIgnoreRange];
    sfIgnoreMasterDetail := [sfSaveIgnoreMasterDetail];
    sfDeltas := [];
    sfDontFilterDeltas := [];
    sfAppend := [];
    sfFieldKind := [sfSaveFieldKind, sfLoadFieldKind];
    sfFromStart := [sfLoadFromStart];
    sfDataTypeHeader := [sfSaveDataTypeHeader, sfLoadDataTypeHeader];
    OnCompress := nil;
    OnDeCompress := nil;
    BufferSize := 16384;
  end;
  //
  // Create internal in-memory tables
  //
  FCSVFile := TCSVFile.Create;
  FGenesisTable := TkbmMemTable.Create(nil);
  FGenesisTable.FormFormat := FDumpFormat;
  CreateTables;
  //
  // Create internal GenesisLocalRepository DataSource exemplar
  //
  FDataSource := TDataSource.Create(nil);
  FDataSource.DataSet := FGenesisTable;
  FDataSource.Enabled := False;
  //
  // Init repositories and genesis file records variables
  //
  Self.ClearData;
  FCurrentFileRecord.Clear;
end;

//------------------------------------------------------------------------------
// destructor TGenesisLocalRepository.Destroy
//   Free memory and destroy exemplar of TGenesisLocalRepository class
// Parameters:
//   None
// Result:
//   None
//------------------------------------------------------------------------------
destructor TGenesisLocalRepository.Destroy;
begin
  Self.ClearData;
  FreeAndNil(FCSVFile);
  FreeAndNil(FDumpFormat);
  FreeAndNil(FGenesisTable);
  //
  inherited Destroy;
  // TODO -cMM: TGenesisLocalRepository.Destroy default body inserted
end;
//------------------------------------------------------------------------------
// procedure TGenesisLocalRepository.AddRepository
//   Add repository information
// Parameters:
//   const NewRepository: TRepository - repository information
// Result:
//   None
//------------------------------------------------------------------------------
procedure TGenesisLocalRepository.AddRepository(const NewRepository:
    TRepository);
var
  nRepIndex: Integer;
begin
  //
  // Add new element to repositories locations' array.
  //   Set new archive element index
  //
  nRepIndex := FRepositoriesCount;
  SetLength(FRepositories, nRepIndex + 1);
  //
  // Assign new repository parameters
  //
  FRepositories[nRepIndex].BaseDir := NewRepository.BaseDir;
  FRepositories[nRepIndex].MinID := NewRepository.MinID;
  FRepositories[nRepIndex].MaxID := NewRepository.MaxID;
  //
  // Increase repositories count variable
  //
  Inc(FRepositoriesCount);
end;
//------------------------------------------------------------------------------
// function TGenesisLocalRepository.CheckFile
//   Define specified file's state in repository
// Parameters:
//   const AbsFileName: AnsiString - absolute AbsFileName of checked file
//   var   FileState: TCheckState - result file state
// Result: Boolean
//   True, if check is proceed. Result is set to FileState variable
//   False, if error occured while check. FileState set to empty subset.
//------------------------------------------------------------------------------
function TGenesisLocalRepository.CheckFile(const AbsFileName: AnsiString; var
    FileState: TFileState; var ID: Integer): Boolean;
const
  BAD_ID = -1;
  ID_ALREADY_PROCESSED = -100;
var
  xFile: TGenesisRecord;
  nFileID: integer;
  nListID: integer;
  //
  //
  //
  cRepositoryDir: AnsiString;
  fsGenesisFile: TFileStream;
  //
  // MD5 calculation variables
  //
  cCRC32String: AnsiString;
  cMD5String: AnsiString;
  xMD5Digest: TMD5Digest;
  //
  // File check results
  //
  bFileState: Boolean;
  bFileProcessed: Boolean;
  bExistInDB: Boolean;
begin
  Assert(FChecking = True,'TGenesisLocalRepository.CheckFile, FChecking flag not set');
  //
  // initialize result variables
  //
  Result := False;
  bFileProcessed := True;
  FileState := [fstBadID];
  ID := BAD_ID;
  //
  // Check if file already processed by absolute filepath. If true, set
  //   ID variable to ID_ALREADY_PROCESSED and exit function
  //
  if FProcessedFiles.Find(AbsFileName, nListID) then begin
    Result := True;
    FileState := [];
    ID := ID_ALREADY_PROCESSED;
    Exit;
  end;
  //
  // Check if specified file exists. Set flag in file state
  //
  bFileState := FileExists(AbsFileName);
  if bFileState then begin
    FileState := FileState + [fstFileExists];
  end;
  //
  // Try to get genesis data record corresponding to specified filename
  //
  xFile.Clear;
  if FCurrentFileRecord.FullName = AbsFileName then begin
    //
    // Record is already selected, read buffered data
    //
    xFile := FCurrentFileRecord;
  end
  else begin
    nFileID := FileNameToID(AbsFileName);
    xFile := Files[nFileID];
  end;
  //
  // Check if filename is found in genesis database. Set fstBadID flag
  //
  ID := xFile.ID;
  if ID = BAD_ID then
    FileState := FileState + [fstBadID]
  else
    FileState := FileState - [fstBadID];
  //
  // if data record for specified file is founded in database,
  //   check file for FileSize, md5, crc32, etc.
  //
  if not (fstBadID in FileState) then begin
    //
    // Check if file is outdated
    //
    if xFile.FileName = EmptyStr then begin
      FileState := FileState + [fstOutdatedFile];
    end;
    //
    // Get local repository directory. If not set, set corresponding flag,
    //   set Result variable and exit function
    //
    cRepositoryDir :=
      GetLocalRepositoryDir(xFile.ID);
    if cRepositoryDir = EmptyStr then begin
      FileState := FileState + [fstOutOfRepIdRange];
      FProcessedFiles.Add(AbsFileName);
      Result := True;
      Exit;
    end;
    //
    // if supplementary check don't set, set result to True and exit function
    //
    bFileProcessed := True;
    if (fstFileExists in FileState) and ((cstFileSize in FFileChecks)
      or (cstMD5 in FFileChecks) or (cstCRC32 in FFileChecks)) then
    begin
      try
        bFileProcessed := False;
        fsGenesisFile :=
          TFileStream.Create(AbsFileName, fmOpenRead + fmShareDenyNone);
        //
        // Check filesize
        //
        if (cstFileSize in FFileChecks) then begin
          bFileState := fsGenesisFile.Size = xFile.FileSize;
          if bFileState then
            FileState := FileState + [fstFileSize];
        end;
        //
        // Check MD5 file checksum
        //
        if (cstMD5 in FFileChecks) then begin
          { TODO -oAdministrator -c :  Test speed of md5 calcul 03.04.2010 18:20:45 }
          //
          // Start test module
          //
          if True then begin
            xMD5Digest := MD5Stream(fsGenesisFile);
            cMD5String := LowerCase(MD5DigestToStr(xMD5Digest));
          end
          else begin
            cMD5String := LowerCase(md5sum(fsGenesisFile, nil));
          end;
          //
          // End test module
          //
          bFileState := cMD5String = xFile.MD5;
          if bFileState then
            FileState := FileState + [fstMD5];
        end;
        //
        // Check CRC32 file checksum
        //
        if (cstCRC32 in FFileChecks) then begin
          { TODO -oAdministrator -c :  Replace template to work code (CRC32 check) 03.04.2010 18:20:45 }
          bFileState := cCRC32String = xFile.CRC32;
          if ({bFileState =} True)  then
            FileState := FileState + [fstCRC32];
        end;
        //
        // File operation is successful, set result to True
        //
        bFileProcessed := True;
      finally
        //
        // Check Result. If False, an error occured while accessing file, set
        //   corresponding flag
        //
        if not bFileProcessed then
          FileState := FileState + [fstAccessDenied];
        FreeAndNil(fsGenesisFile);
      end;
    end;
  end
  //
  // record for specified file not located in DB, check file for other parameters
  //
  else begin
    //
    // Current file is extra to database
    //
    FileState := FileState + [fstExtraFile];
    Result := True;
  end;
  //
  // If Result is True, add AbsFileName to processed files list to
  //   eliminate duplicate processing
  //

  { TODO -oMichel -c : Add Result variable processing, temp code now 30.11.2010 20:49:45 }
  Result := True;
  if bFileProcessed then begin
    FProcessedFiles.Add(AbsFileName);
  end;
end;
//------------------------------------------------------------------------------
// procedure TGenesisLocalRepository.ClearData
//   ClearData local repository data and reinitialize in-memory storage classes
// Parameters:
//   const GenesisID: Integer
// Result: AnsiString (Type)
//   None
//------------------------------------------------------------------------------
procedure TGenesisLocalRepository.ClearData;
begin
//  ClearGenesisFiles;
  ClearRepositories;
end;

//------------------------------------------------------------------------------
// procedure TGenesisLocalRepository.ClearGenesisFiles
//   Empty in-memory tables
// Parameters:
//   None
// Result: AnsiString (Type)
//   None
//------------------------------------------------------------------------------
procedure TGenesisLocalRepository.ClearGenesisFiles;
begin
  FGenesisTable.EmptyTable;
end;
//------------------------------------------------------------------------------
// function TGenesisLocalRepository.GetLocalRepositoryDir
//   Returns local repository root directory for input GenesisID
// Parameters:
//   const GenesisID: integer - input GenesisID value
// Result: AnsiString
//   Returns local repository root directory for input GenesisID
//------------------------------------------------------------------------------
function TGenesisLocalRepository.GetLocalRepositoryDir(const GenesisID:
    Integer): AnsiString;
var
  nRepIndex: Integer;
begin
  Result := EmptyStr;
  //
  // Search first repository, where (MinID <= GenesisID) and (GenesisID <= MaxID)
  //
  for nRepIndex := 0 to RepositoriesCount - 1 do begin
    if (FRepositories[nRepIndex].MinID <= GenesisID) and
      (GenesisID <= FRepositories[nRepIndex].MaxID) then
    begin
      Result :=
        IncludeTrailingPathDelimiter(FRepositories[nRepIndex].BaseDir);
      Break;  // for nRepIndex := 0 ...
    end;
  end;
end;
//------------------------------------------------------------------------------
// function TGenesisLocalRepository.GetLocalRepositoryPath
//   Returns local repository root directory for input GenesisID
// Parameters:
//   const GenesisID: integer - input GenesisID value
// Result: AnsiString
//   Returns local repository root directory for input GenesisID
//------------------------------------------------------------------------------
function TGenesisLocalRepository.GetLocalRepositoryPath(
    const GenesisID: Integer): AnsiString;
var
  nRepIndex: Integer;
begin
  Result := EmptyStr;
  //
  // Search first repository, where (MinID <= GenesisID) and (GenesisID <= MaxID)
  //
  for nRepIndex := 0 to FRepositoriesCount - 1 do begin
    if (FRepositories[nRepIndex].MinID <= GenesisID) and
      (GenesisID <= FRepositories[nRepIndex].MaxID) then
    begin
      Result :=
        IncludeTrailingPathDelimiter(FRepositories[nRepIndex].BaseDir);
      Break;  // for nRepIndex := 0 ...
    end;
  end;
end;
//------------------------------------------------------------------------------
// function TGenesisLocalRepository.CreateTable
//   Create table data structure (fields, indices)
// Parameters:
//   var   Table      : TkbmMemTable        - TkbmMemTable exemplar to proceed
//   const TableName  : WideString          - table name
//   const FieldDesc  : array of TFieldDesc - fields specification
//   const IndexDesc  : array of TIndexDesc - indices specification
// Result: Boolean
//   True, if table structure created successfully; False otherwise
//------------------------------------------------------------------------------
function TGenesisLocalRepository.CreateTable(var Table: TkbmMemTable; const TableName: WideString;
  const FieldDesc: array of TFieldDesc;
  const IndexDesc: array of TIndexDesc): Boolean;
var
  i: Integer;
begin
  Result := False;
  if Table = nil then begin
    Exit;
  end;
  Table.Active := False;
  Table.DeleteTable;
  try
    if not (Table is TkbmMemTable) then begin
//      Table.TableName := TableName;
//      Table.DatabaseName := FDBMain.DatabaseName;
    end;
    //
    // Fill fields data structure
    //
    for i := 0 to High(FieldDesc) do
      Table.FieldDefs.Add(
        FieldDesc[i].Name,
        FieldDesc[i].DataType,
        FieldDesc[i].Size,
        FieldDesc[i].Required
      );
    //
    // Fill indices data structure
    //
    for i := 0 to High(IndexDesc) do
      Table.IndexDefs.Add(
        IndexDesc[i].Name,
        IndexDesc[i].Fields,
        IndexDesc[i].Options
      );
    //
    // Create table with specified data structure
    //
    Table.CreateTable;
    Result := True;
  finally
  end;
end;
//------------------------------------------------------------------------------
// function TGenesisLocalRepository.CreateTables
//   Creates internal DB tables
// Parameters:
//   None
// Result: Boolean
//   True, if internal tables created;
//   False, if error occured while creating tables
//------------------------------------------------------------------------------
function TGenesisLocalRepository.CreateTables: Boolean;
var
  bTableCreated: Boolean;
begin
  Result := True;
  //
  // Create FGenesisTable, set result
  //
  bTableCreated := CreateTable(FGenesisTable, 'genesis', GenesisTableFields,
    GenesisTableIndexes);
  Result := Result and bTableCreated;
end;

//------------------------------------------------------------------------------
// procedure TGenesisLocalRepository.ClearRepositories
//   ClearData local repositories location's array
// Parameters:
//   None
// Result:
//   None
//------------------------------------------------------------------------------
procedure TGenesisLocalRepository.ClearRepositories;
begin
  SetLength(FRepositories,0);
  FRepositoriesCount := 0;
end;

procedure TGenesisLocalRepository.EndCheck;
begin
  FreeAndNil(FProcessedFiles);
  FChecking := False;
end;

//------------------------------------------------------------------------------
// function TGenesisLocalRepository.FileNameToID
//   Returns genesis file id corresponding to specified filename (absolute)
// Parameters:
//   const FileName : AnsiString  - full filename (include absolute path)
// Result: Integer
//   Genesis id, if file exists; -1 otherwise
//------------------------------------------------------------------------------
function TGenesisLocalRepository.FileNameToID(const FileName: AnsiString):
    Integer;
var
  i: Integer;
  cFileName: AnsiString;
  bLocate: Boolean;
begin
  Result := -1;
  //
  // Convert path to relative (delete repository root dir, if match)
  //
  cFileName := FileName;
  for i := 0 to RepositoriesCount - 1 do begin
    //
    // Delete repository root path from full filename
    //
    cFileName :=
      AnsiReplaceStr(cFileName, IncludeTrailingPathDelimiter(Repositories[i].BaseDir), '');
    //
    // if succeed (length of filename changed), exit cycle
    //
    if Length(cFileName) <> Length(FileName) then
      Break;
  end;
  //
  // Replace path delimiter char by char used in csv file
  //
  cFileName := AnsiReplaceStr(cFileName, '\','/');
  //
  // Change index of database to 'FileName_Index' and search
  //
  FGenesisTable.IndexName := 'FileName_Index';
  bLocate := FGenesisTable.Locate('FileName',cFileName, [loCaseInsensitive]);
  if bLocate then begin
    Result := FGenesisTable['ID'];
  end;
end;
//------------------------------------------------------------------------------
// function TGenesisLocalRepository.GetAbsoluteFileName
//   Generate absolute path to filename
// Parameters:
//   const BaseDir: AnsiString - root repository folder
//   const RelFileName: AnsiString - relative path to file inside repository
// Result: AnsiString
//   Absolute path to file into repository
//------------------------------------------------------------------------------
function TGenesisLocalRepository.GetAbsoluteFileName(const BaseDir,
    RelFileName: AnsiString): AnsiString;
var
  cAbsFileName: AnsiString;
begin
  cAbsFileName := IncludeTrailingPathDelimiter(BaseDir) + RelFileName;
  Result := AnsiReplaceStr(cAbsFileName,'/', '\');
end;
//------------------------------------------------------------------------------
// function TGenesisLocalRepository.GetDataSource
//   Returns link to internal locl repository datasource
// Parameters:
// -
// Result: TDataSource
//   Pointer to Internal local repository data source
//------------------------------------------------------------------------------
function TGenesisLocalRepository.GetDataSource: TDataSource;
begin
  Result := FDataSource;
end;

//------------------------------------------------------------------------------
// function TGenesisLocalRepository.GetGenesisRecord
//   Return
// Parameters:
//   Index: Integer   
// Result: TGenesisRecord (Type)
//   None
//------------------------------------------------------------------------------
function TGenesisLocalRepository.GetGenesisRecord(FileID: Integer):
    TGenesisRecord;
var
  bDataLoaded: Boolean;
begin
  //
  // Check if data of record with specified FileID is already loaded
  //
  bDataLoaded := (FileID > 0) and (FileID = FCurrentFileRecord.ID);
  if bDataLoaded then begin
    Result := FCurrentFileRecord;
    Exit;
  end;
  //
  // Load file related data to genesis record structure
  //
  bDataLoaded := LoadGenesisRecord(FileID);
  if not bDataLoaded then
    FCurrentFileRecord.Clear;
  //
  // Set result and exit function
  //
  Result := FCurrentFileRecord;
end;
//------------------------------------------------------------------------------
// function TGenesisLocalRepository.GetRepositories
//   Get local repository parameters
// Parameters:
//   Index: integer - index of local repository
// Result: TRepository
//   Local repository parameters record
//------------------------------------------------------------------------------
function TGenesisLocalRepository.GetRepositories(Index: Integer): TRepository;
var
  xRepository: TRepository;
begin
  //
  // Init temporary repository variable
  //
  xRepository.Clear;
  //
  // if index is not out of array bounds, get repository record
  //
  if (0 <= Index) and (Index < FRepositoriesCount) then begin
    xRepository := FRepositories[Index];
  end;
  //
  // Assign repository record to Result variable and exit function
  //
  Result := xRepository;
end;
//------------------------------------------------------------------------------
// TGenesisLocalRepository.GetTableActive
//   Returns internal data table state
// Parameters:
// -
// Result: Boolean
//   Returns current data table state
//------------------------------------------------------------------------------
function TGenesisLocalRepository.GetTableActive: Boolean;
begin
  Result := FTableActive;
end;

function TGenesisLocalRepository.GetTotalFilesCount: Integer;
begin
  // TODO -cMM: TGenesisLocalRepository.GetTotalFilesCount default body inserted
  Result := FTotalFilesCount;
end;

//------------------------------------------------------------------------------
// function TGenesisLocalRepository.LoadGenesisRecord
//   Load book data into internal data structure
// Parameters:
//   const FileID : Integer  - file id of specified genesis file.
//                             If set to -1, then load data from current db table record
// Result: Boolean
//   True, if record data loaded successfully, False otherwise
//------------------------------------------------------------------------------
function TGenesisLocalRepository.LoadGenesisRecord(const FileID: Integer):
    Boolean;
var
  xGenesisRecord: TGenesisRecord;
begin
  Result := False;
  //
  // Check if database table is active
  //
  if not FGenesisTable.Active then begin
    { TODO -oAdministrator -c : Add error processing 11.08.2010 17:56:07 }
    FCurrentFileRecord.Clear;
    Exit;
  end;
  //
  // Create and initialize temporary file's record variable
  //
  xGenesisRecord.Clear;
  xGenesisRecord.ID := -1;
  //
  // Check if needed table's record already selected. If not, select it
  //
  if not (FileID = FGenesisTable['ID']) then begin
    FGenesisTable.IndexName := 'ID_Index';
    FGenesisTable.Locate('id',FileID,[]); //[loCaseIncensitive]
    //
    // Exit routine, if record with specified id not found in table
    //
    if not FGenesisTable.Found then begin
      { TODO -oAdministrator -c : Add error processing 11.08.2010 17:56:07 }
      FCurrentFileRecord.Clear;
      Exit;
    end;
  end;
  //
  // Get local repository absolute path
  //
  xGenesisRecord.RootDir := GetLocalRepositoryDir(FileID);
  { TODO -oAdministrator -c : test speed with diferent index options 11.08.2010 23:31:09 }
  //
  // Fill genesis structure file related fields
  //
  if FGenesisTable['ID'] = FileID then
    with xGenesisRecord do begin
      ID := FGenesisTable.FieldByName('id').AsInteger;
      FileName := FGenesisTable.FieldByName('FileName').AsString;
      { TODO -oAdministrator -c : FileSize must have Int64 value 11.08.2010 19:24:33 }
      FileSize := FGenesisTable.FieldByName('FileSize').AsInteger;
      MD5 := LowerCase(FGenesisTable.FieldByName('MD5').AsString);
      CRC32 := LowerCase(FGenesisTable.FieldByName('CRC32').AsString);
//      IsLocal := FGenesisTable['Local'];
      //
      // Assign result to internal genesis structure, set result and exit function
      //
      FCurrentFileRecord := xGenesisRecord;
      Result := True;
    end
  else begin
    //
    // Set Result to False, if error occured while selection of book
    //
    FCurrentFileRecord.Clear;
    Result := False;
  end;
end;
//------------------------------------------------------------------------------
// procedure TGenesisLocalRepository.LoadTableFromCSV
//   Load data from CSV File into specified DataSource
// Parameters:
//   const CSVFileName: AnsiString - csv filename with absolute path
// Result: 
//   None
//------------------------------------------------------------------------------
function TGenesisLocalRepository.LoadDataFromCSV(const CSVFileName:
    AnsiString): Boolean;
var
  dtTime: TDateTime;
  bExport: Boolean;
begin
  dtTime := Now;
  //
  // Load data from CSV-file into internal CSV class
  //
  FCSVFile.Clear;
  FCSVFile.LoadCSVFile(CSVFileName);
  //
  // Check if in-memory table is open, ClearData existing data
  //
  if not FGenesisTable.Active then
    FGenesisTable.Active := True;
  FGenesisTable.EmptyTable;
  //
  // Export data from CSV into in-memory table according to it fields structure
  //   ClearData CSV class data and free memory
  //
  bExport := FCSVFile.ExportToTable(FGenesisTable);
  FCSVFile.Clear;
  //
  // Set table state and statistic properties values
  //
  FDataLoaded := bExport;
  FTableActive := FGenesisTable.Active;
  FTotalFilesCount := FGenesisTable.RecordCount;
  //
  MessageBox(0, PChar('Time elapsed = ' + TimeToStr(Now - dtTime)), '', MB_OK);
end;
//------------------------------------------------------------------------------
// function TGenesisLocalRepository.LoadDataFromDump
//   Load data from dump to in-memory table
// Parameters:
//   const DumpFile : AnsiString  - path to dump data file
// Result: Boolean
//   True, if data loaded; False otherwise
//------------------------------------------------------------------------------
function TGenesisLocalRepository.LoadDataFromDump(const DumpFile: AnsiString):
    Boolean;
begin
  Result := False;
  //
  // Check if file exists; exit, if file not found
  //
  if not FileExists(DumpFile) then
    Exit;
  //
  // try to load data into in-memory table
  //
  try
    //
    // Clear exist table data
    //
    FGenesisTable.Active := True;
    FGenesisTable.EmptyTable;
    //
    // Load data to in-memory table from dump
    //
    FGenesisTable.LoadFromFileViaFormat(DumpFile,FGenesisTable.FormFormat);
    //
    FDataLoaded := True;
    TableActive := True;
    Result := True;
  except
    TableActive := False;
    FDataLoaded := False;
  end;
end;
//------------------------------------------------------------------------------
// function TGenesisLocalRepository.SaveDataToDump
//   Save data from in-memory table to dump data file
// Parameters:
//   const DumpFile : AnsiString  - path to dump data file
// Result: Boolean
//   True, if operation succeed; False otherwise
//------------------------------------------------------------------------------
function TGenesisLocalRepository.SaveDataToDump(const DumpFile: AnsiString):
    Boolean;
begin
  Result := False;
  //
  // Check if data table is active. If not, set result and exit function
  //
  if not TableActive then begin
    Exit;
  end;
  //
  // Check if specified filepath exists; if not, create directory or exit, if
  //   not successful
  //
  if not DirectoryExists(ExtractFilePath(DumpFile)) then begin
    if not ForceDirectories(ExtractFilePath(DumpFile)) then
      Exit;
  end;
  //
  // Save data to dump from in-memory table
  //
  try
    FGenesisTable.SaveToFileViaFormat(DumpFile,FGenesisTable.FormFormat);
    Result := True;
  finally
  end;
end;

procedure TGenesisLocalRepository.SetChecking(const Value: Boolean);
begin
  FChecking := Value;
end;

//------------------------------------------------------------------------------
// procedure TGenesisLocalRepository.SetFileChecks
//   Assign value to FileChecks property
// Parameters:
//   const Value: TCheckState - input value
// Result: 
//   None
//------------------------------------------------------------------------------
procedure TGenesisLocalRepository.SetFileChecks(const Value: TCheckState);
begin
  FFileChecks := Value;
end;
//------------------------------------------------------------------------------
// procedure TGenesisLocalRepository.SetGenesisFiles
//   Set value to element of genesis file's properties array
// Parameters:
//   Index: integer - index of genesis file
//   const Value: TGenesisRecord - genesis file parameters
// Result:
//   None
//------------------------------------------------------------------------------
procedure TGenesisLocalRepository.SetGenesisFiles(FileID: Integer; const Value:
    TGenesisRecord);
begin
  //
  // if Index is out of genesis file's array bounds, do nothing
  //
//  if (0 <= Index) and (Index < FGenesisFilesCount) then begin
//    FGenesisFiles[Index] := Value;
//  end;
end;
//------------------------------------------------------------------------------
// procedure TGenesisLocalRepository.SetRepositories
//   Edit local repository parameters
// Parameters:
//   Index: integer - local repository index
//   Value: TRepository - local repository parameters record
// Result:
//   None
//------------------------------------------------------------------------------
procedure TGenesisLocalRepository.SetRepositories(Index: Integer; const Value:
    TRepository);
begin
  //
  // if Index is out of repositories array bounds, do nothing
  //
  if (0 <= Index) and (Index < FRepositoriesCount) then begin
    FRepositories[Index] := Value;
  end;
end;
//------------------------------------------------------------------------------
// TGenesisLocalRepository.SetTableActive
//   Set state of internal data table
// Parameters:
//   const Value : Boolean  - desired table state
// Result: None
//   None
//------------------------------------------------------------------------------
procedure TGenesisLocalRepository.SetTableActive(const Value: Boolean);
begin
  if not FDataLoaded then begin
    FTableActive := False;
    Exit;
  end;
  //
  // try to set table to desired state
  //
  if Value <> FTableActive then begin
    FDataSource.Enabled := Value;
    FGenesisTable.Active := Value;
    FTableActive := FGenesisTable.Active;
    FDataLoaded := Value;
  end;
  //
  // Reassign dataset to internal datasource (is missed on dataset deactivation)
  //
  if (Value = True) then 
    FDataSource.DataSet := FGenesisTable;
end;

//------------------------------------------------------------------------------
// procedure TGenesisLocalRepository.StartCheck
//    Prepare class for start file checks operations
// Parameters:
//    None
// Result: None
//------------------------------------------------------------------------------
procedure TGenesisLocalRepository.StartCheck;
begin
  if FProcessedFiles = nil then begin
    FProcessedFiles := TStringList.Create;
  end;
  FProcessedFiles.Clear;
  FProcessedFiles.Duplicates := dupIgnore;
  FProcessedFiles.Sorted := True;
  //
  FChecking := FProcessedFiles <> nil;
end;
//==============================================================================
// TGenesisRecord routines
//==============================================================================
//------------------------------------------------------------------------------
// function TGenesisRecord.CheckRecord
//   Check book record's fields with defined limits
// Parameters:
//   None
// Result: Boolean
//   True, if book record is correct; False otherwise
//------------------------------------------------------------------------------
function TGenesisRecord.CheckRecord: Boolean;
var
  bCorrect: Boolean;
begin
  Result := False;
  //
  // Check each parameters conditions
  //
  bCorrect := Self.ID > 0;
  bCorrect := bCorrect and (Length(Self.MD5) = 32);
  //
  // Set result and exit function
  //
  Result := bCorrect;
end;
//------------------------------------------------------------------------------
// procedure TGenesisRecord.Clear
//   Assign default values to fields of TGenesisRecord record
// Parameters:
//   None
// Result:
//   None
//------------------------------------------------------------------------------
procedure TGenesisRecord.Clear;
begin
  ID := -1;
  FileName := EmptyStr;
  FileSize := 0;
  MD5 := EmptyStr;
  CRC32 := EmptyStr;
  IsLocal := False;
end;
//------------------------------------------------------------------------------
// function TGenesisRecord.FullName
//   Returns genesis file full name
// Parameters:
//   None
// Result: AnsiString
//   Absolute file path to genesis file with specified id
//------------------------------------------------------------------------------
function TGenesisRecord.FullName: AnsiString;
var
  cResult: AnsiString;
begin
  cResult := IncludeTrailingPathDelimiter(RootDir) + FileName;
  Result := AnsiReplaceStr(cResult, '/','\');
end;
//==============================================================================
// TRepository routines
//==============================================================================
//------------------------------------------------------------------------------
// procedure TRepository.Clear
//   Assign default values for fields of TRepository record
// Parameters:
//   None
// Result:
//   None
//------------------------------------------------------------------------------
procedure TRepository.Clear;
begin
  BaseDir := EmptyStr;
  MinID := 0;
  MaxID := 0;
end;


end.
